#-------------------------------------------------------------------------------
# Name:        Game
# Purpose:
#
# Author:      Theo
#
# Created:     10/02/2012
# Copyright:   (c) Monxcleyr Productions 2012
# Licence:     GPL v3
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import pygame, sys, cPickle, zlib, os, logging
import config, key, sound, debug, options, common, menu, pause, star, world, saves, \
       placeholders, dialog, fonthandler, credit
from ..gui import label
from ..tools import videotools, pyconsole, functions, syntax
##from ..tools.functions import *
##from ..tools.syntax import *
from pygame.locals import *
from . import __version__ #Import engine version

###global rferences to Game, so the console can acces it. b;ah hacks yay!
##game =

class Game:
  def __init__(self):
    # Class containing common engine variables
    logging.info('Engine Version %s' % __version__)
    self.common = common.Common()
    logging.info ('%s Version %s' % (self.common.name, self.common.version))
    self.SW = self.common.reswidths[self.common.settings.res_setting] # Load up the defined resolution
    self.SH = int(self.SW / self.common.scale)
    self.size = (self.SW, self.SH)

    try:
      icon = pygame.image.load('resources/graphics/common/icon.png')
      icon.set_colorkey((255,0,255))
      pygame.display.set_icon(icon)
    except:
      pass

    # The various surfaces the game will use
    self.menuS = pygame.Surface(self.common.initsize)
    self.menuS.set_colorkey((255,0,255))
    self.optionS = pygame.Surface(self.common.initsize)
    self.optionS.set_colorkey((255,0,255))
    self.gameS = pygame.Surface(self.common.initsize)
    self.gameS.set_colorkey((255,0,255))
    self.mapS = pygame.Surface(self.common.initsize)
    self.titleS = pygame.Surface(self.common.initsize)
    # Everything is draw onto preS before drawn to the screen
    self.preS = pygame.Surface((self.SW, self.SH))
    # Debug Screen so it can be drawn on top
    self.debugS = pygame.Surface(self.common.initsize, SRCALPHA)
    self.screen = pygame.display.set_mode((self.SW, self.SH), SRCALPHA|DOUBLEBUF|HWSURFACE)
    if self.common.settings.fullscreen == True:
      self.screen = pygame.display.set_mode(self.size, SRCALPHA|DOUBLEBUF|FULLSCREEN|HWSURFACE)
    pygame.display.set_caption('#HJ Game Engine Initializing...')
    self.poll = 0
    self.quit = 0
    # Boolean that holds if the size of the display screen has changed
    self.scalechanged = True
    # THis holds if the game is running in either debug mode, set by Main
    self.debugstates = (False, False, False)
    self.count = 0
    self.paused = False

    self.dialog = dialog.Dialog(self.common, self.common.imagehandler)

    #console
    self.console = pyconsole.Console(
                                    self.preS,
                                    (0,0,600,250),
                                    functions=functions.functions,
                                    key_calls={"d":sys.exit},
                                    syntax={syntax.re_add:syntax.console_add, syntax.re_function:syntax.console_func}
                                    )
    self.console.game = self
    self.console.setBackground('resources/graphics/console/console.png')

    """"""
    # placeholders
    self.minigame = placeholders.minigame()


    # Modes:
    # 0 = Main Menu
    # 1 = Game
    # 2 = New Game
    # 3 = Load Game
    # 4 = Options Menu
    # 5 = Pause Screen
    # 6 = Developer Console
    # 7 = Updater
    # 8 = Return to previous mode
    # 9 = Credits
    # 10 = Save Screen
    # 11 = Mini Game
    self.mode = 0
    self.prevmode = 0
    # List of named modes, for debug screen
    self.modes = {0: 'Main Menu', 1:'Game', 2:'New Game', 3:'Load Game', 4:'Options Menu', 5:'Pause', 6:'Console', \
                  7:'Updater', 8:'Previous', 9:'Credits', 10:'Save Screen', 11:'Mini Game'}
    self.console.setvar('modes', self.modes)

    # Create debug class
    self.debug = debug.Debug(False, self.preS, (5,5), (5,5), 4, __version__, self.console)

    # copy debug to common
    self.common.debug = self.debug

    # Starfrield testing
##    self.stars = star.Starlayer(self.gameS)
##    self.stars.setSpeed((0, 1))
    self.starfield = star.StarField(self.gameS)

    # basic world class
##    self.world = world.World()

  def saveWorld(self, saveslot, load=True):
    """ PERMADEATH OR BUST, DO NOT USE THIS EVER """
    self.world.saveslot = saveslot
    self.world.imagehandler.reset()
    self.common.debug.console = None
    filename = 'Saves'
    f = open(os.path.join(filename, self.world.saveslot + '.sav'), 'wb')
    object1 = zlib.compress(cPickle.dumps(self.world, cPickle.HIGHEST_PROTOCOL),9)
    cPickle.dump(object1,f,cPickle.HIGHEST_PROTOCOL)
    f.close()

    logging.info('Game slot %s saved', saveslot)
    if load == True:
      self.loadSave(saveslot)
##    self.world.loadPhysics(self.screen)
##    self.world.initPymunk(self.screen)

  def loadSave(self, saveslot):
    """ LOAD A SAVE LOLOL """
    filename = 'Saves'
    try:
      f = open(os.path.join(filename, saveslot + '.sav'),'rb')
      obj = cPickle.load(f)
      self.world = cPickle.loads(zlib.decompress(obj))
      f.close()
      logging.info('Game slot %s loaded', saveslot)
      self.mode = 1
    except IOError as e:
      logging.critical('Failed to load saveslot %s' % saveslot)

    self.common.inittime = int(pygame.time.get_ticks() / 1000)
    self.savescreen.reset()
    self.common.debug.console = self.console

    if self.music.getCurrentLoop() != 'ambiance':
      self.music.loadQueue('ambiance')
      self.music.playQueue()


  def newGame(self, name, slot):
    """ Creates a WHOLE NEW GAME! :O """
    logging.info('Creating new game %s in slot %s' % (name, slot))
    self.world = world.World(self.common)
    self.world.savename = name
    self.world.saveslot = str(slot)
    self.saveWorld(str(slot))
    self.common.time = 0
    self.common.prevtime = 0

##    self.world.testDialog()

  def postInit(self):
    """ This is a work around method for loading sounds and such after using pygame.movie
    to play the splash screens. """

    self.common.settings.activate()
    self.sound = sound.effect('resources/config/sounds.ini', self.common.settings, self.debug)
    self.music = sound.music('resources/config/sounds.ini', self.debug)
    self.console.setvar('musicloops', self.music.loops)
    self.menu = menu.Menu(self.menuS, 'menu.ini', self.common, self.sound, self.music, self.debug)
    self.options = options.Options(self.optionS, 'options.ini', self.common, self.sound, self.music, self.debug, self.size)
    # Pause Screen class
    self.pause = pause.Pause('pause.ini', self.common, self.sound, self.music, self.debug)
    self.savescreen = saves.SaveScreen(self.common, self.sound, self.music, self.debug)
    self.credits = credit.Credits(self.common, self.music)

  def setMode(self, mode):
    """ Used to change the game's mode.
    Same as using game.mode = x """
    if self.mode != 8:
      if self.prevmode != mode and mode != self.mode:
        self.prevmode = self.mode
    self.mode = mode

  def usePrevMode(self):
    self.mode = self.prevmode
##    self.setMode(self.prevmode)
##    self.setMode(self.prevmode)

  def checkQuit(self):
    if self.quit == 1:
      return True
    else:
      return False

  def updatePreS(self, surface):

    # Draws the game to the main screen
    if surface.get_size() != self.screen.get_size():
      pygame.transform.smoothscale(surface, self.size, self.preS)
    else:
      self.preS.blit(surface, (0,0))

  def getDirtyRects(self):

    """ Convert rects from world positions to screen positions """
    scale = self.preS.get_size()[0] / float(self.screen.get_size()[0])
    returnlist = []

    for rect in self.world.objecthandler.rects:
      if rect:
        rect.topleft = (rect.topleft[0] + self.world.offset[0], rect.topleft[1] + self.world.offset[1])
        posx = rect.left * scale
        posy = rect.top * scale
        width = rect.width * scale
        height = rect.height * scale
        # add some padding to the rect to make up for slight calculation errors
        returnlist.append(pygame.Rect(posx - 2, posy - 2, width + 4, height + 4))

    return returnlist


#-------------------------------------------------------------------------------
#--------------------------------- Game Loop -----------------------------------
#-------------------------------------------------------------------------------


  def runGame(self, time, mouse):


    # temp ambience sound
    if pygame.mixer.music.get_busy() == 0:
        self.music.playQueue()

    # Make sure sounds aren't paused
    self.sound.unpauseAll()

    # TODO: Remember to port this time tracking stuff to the engine
    self.common.prevtime = self.common.time
    self.common.time = (int(pygame.time.get_ticks() / 1000) - self.common.inittime)
    self.common.frametime = time
    if self.common.prevtime <= self.common.time:
      self.world.timeplayed += self.common.time - self.common.prevtime
      # Game time doesn't tick during minigames, dialogs or when paused
      if not self.world.computer.minigamecomplete and not self.paused and not self.world.dialog.checkActive(1) and not self.console.active:
        self.world.gametime += self.common.time - self.common.prevtime

    if self.paused:
      self.paused = False

    self.gameS.fill((0,0,0))

    # Update keys
    key = pygame.key.get_pressed()

    # Bring up pause screen
    if self.common.keyState.check(self.common.key.bind['pause'])[0] == True:
      self.common.keyState[self.common.key.bind['pause']][0] = False
      if key[self.common.key.bind['pause']]:
        self.setMode(5)
        self.paused = True
        self.sound.pauseAll()


#------------------------------- Update things, yo!-----------------------------

    if not self.console.active:
      # Update dialogs
      self.world.dialog.update(self.world.gametime)

      # Update Computer
      self.world.computer.update(self.world.gametime, self.common, self.world.objecthandler)

      if self.world.dialog.checkActive(1):
        self.world.objecthandler.getObjectByType('player').attributes['blocked'] = True
      else:
        self.world.objecthandler.getObjectByType('player').attributes['blocked'] = False

      # update objects
      self.world.objecthandler.update(self.common, self.world.computer)

      # update collisions
      self.world.updateCollisions(self.world.objecthandler.getObject('player'))

      # Check if we need to activate the minigame of justice
      for obj in self.world.objecthandler.getObjectAncestorList('console'):
        if obj.attributes['beingused']:
          self.mode = 11

      if self.world.computer.emergency['running']:
        self.sound.playLoop('alarm', self.common.settings)
      else:
        self.sound.stopLoop('alarm')

#----------------------------- Draw game stuff! --------------------------------

    # Draw stars
    self.starfield.draw(self.gameS, self.common)

    # Draw objects:
    self.world.updateOffset(self.gameS, self.sound, self.common.settings)
    self.world.objecthandler.draw(self.gameS, self.world.offset, self.common)

    # Draw debug
    self.drawDebug()

    # Draw Dialog Boxes
    self.world.dialog.draw(self.gameS, mouse, self.sound, self.common)

    # Draw debug mouse
    self.drawDebugMouse(mouse)


##    if self.world.computer.status['navigation'] > 10:
##      self.mode = 9


#-------------------------------------------------------------------------------


  def drawDebug(self):

    """ Draw debug stuff, yo. yoloswagdebugitalldayerryday """

    # Show collision hitboxes if in -debug mode
    if self.debugstates[0]:
      for tile in self.world.objecthandler.getObjectByType('ship').attributes['tiles']:
        rect = tile.getRect()
        rect.topleft = (rect.topleft[0] + self.world.offset[0], rect.topleft[1] + self.world.offset[1])
        pygame.draw.rect(self.gameS, (255,255,0), rect, 1)


      # Gather objects that use bounding rects for collisions
      objs = []
      for obj in self.world.objecthandler.getObjectAncestorList('sprite'):
        objs.append(obj)

      # draw collision bounding rects
      for obj in objs:
        if obj.attributes['solid'] and obj.attributes['visable'] and obj.type != 'player':
          rect = obj.getBoundingRect()
          rect.topleft = (rect.topleft[0] + self.world.offset[0], rect.topleft[1] + self.world.offset[1])
          pygame.draw.rect(self.gameS, (255,255,0), rect, 1)


      for obj in self.world.objecthandler.getObjectAncestorList('console'):
        if obj.attributes['visable']:
          rect = obj.getActivateRect()
          rect.topleft = (rect.topleft[0] + self.world.offset[0], rect.topleft[1] + self.world.offset[1])
          if obj.attributes['playerinrange']:
            pygame.draw.rect(self.gameS, (255,100,0), rect, 1)
          elif obj.attributes['required']:
            pygame.draw.rect(self.gameS, (0,255,255), rect, 1)
          else:
            pygame.draw.rect(self.gameS, (255,200,0), rect, 1)




  def drawDebugMouse(self, mouse):

    """ Draw the debug mouse """

    if self.debugstates[0]:

      # draw the mouse, and a tooltip giving the world coordinates.
      self.gameS.blit(self.debug.mouse, (mouse[0][0] - 5, mouse[0][1] - 5))
      self.debug.mouselabel.pos = (mouse[0][0] + 7, mouse[0][1] + 7)
      self.debug.mouselabel.screen = self.gameS
      self.debug.mouselabel.setText(str((int(mouse[0][0] - self.world.offset[0]), int(mouse[0][1] - self.world.offset[1]))))
      self.debug.mouselabel.draw()

    if self.console.active:
      self.gameS.blit(self.debug.mouse, (mouse[0][0] - 5, mouse[0][1] - 5))



